/*
 *  tracker/EnvelopeEditorControl.h
 *
 *  Copyright 2009 Peter Barth
 *
 *  This file is part of Milkytracker.
 *
 *  Milkytracker is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Milkytracker is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef ENVELOPEEDITORCONTROL__H
#define ENVELOPEEDITORCONTROL__H

#include "BasicTypes.h"
#include "EditorBase.h"
#include "Control.h"
#include "Event.h"

class EnvelopeEditorControl : public PPControl, public EventListenerInterface, public EditorBase::EditorNotificationListener
{
    private:
        PPColor ourOwnBorderColor;
        PPColor backgroundColor;

        bool border;
        const PPColor* borderColor;

        class PPScrollbar * hScrollbar;

        class PPControl * caughtControl;
        bool controlCaughtByLMouseButton, controlCaughtByRMouseButton;

        class PPContextMenu * editMenuControl;
        class PPContextMenu * subMenuAdvanced;

        class EnvelopeEditor * envelopeEditor;
        const struct TEnvelope* envelope;

        // extent
        pp_int32 xMax;
        pp_int32 yMax;
        PPPoint currentPosition;

        pp_int32 xScale;

        // panning envelopes show the zero axis
        bool showVCenter;

        pp_int32 startPos;
        pp_int32 visibleWidth;
        pp_int32 visibleHeight;
        bool hasDragged;

        // selection
        pp_int32 selectionTicker;

        // Envelope counters
        struct ShowMark
        {
            pp_int32 pos;
            pp_int32 intensity;
            pp_int32 panning;
        }* showMarks;

        enum MenuCommandIDs
        {
            MenuCommandIDXScale = 99,
            MenuCommandIDYScale
        };
    public:
        EnvelopeEditorControl (pp_int32 id,
                               PPScreen* parentScreen,
                               EventListenerInterface* eventListener,
                               const PPPoint& location,
                               const PPSize& size,
                               bool border = true);

        ~EnvelopeEditorControl ();

        void setColor(const PPColor& color) {
            backgroundColor = color;
        }

        void setBorderColor(const PPColor& color) {
            borderColor = &color;
        }

        void setxMax(pp_int32 xMax) {
            this->xMax = xMax; adjustScrollbars();
        }
        pp_int32 getxMax() {
            return xMax;
        }

        void setyMax(pp_int32 yMax) {
            this->yMax = yMax; adjustScrollbars();
        }
        pp_int32 getyMax() {
            return yMax;
        }

        void attachEnvelopeEditor(EnvelopeEditor* envelopeEditor);

        const TEnvelope * getEnvelope() {
            return envelope;
        }
        EnvelopeEditor * getEnvelopeEditor() {
            return envelopeEditor;
        }

        void setShowVCenter(bool b) {
            showVCenter = b;
        }

        void paintGrid(PPGraphicsAbstract* graphics, pp_int32 xOffset, pp_int32 yOffset);

        // from PPControl
        virtual void paint(PPGraphicsAbstract* graphics);
        virtual pp_int32 dispatchEvent(PPEvent* event);
        virtual pp_int32 handleEvent(PPObject* sender, PPEvent* event);

        virtual void setSize(const PPSize& size);
        virtual void setLocation(const PPPoint& location);
    protected:
        virtual void translateCoordinates(PPPoint& cp)
        {
            PPControl::translateCoordinates(cp);
            cp.x -= 2;
            cp.y -= 2;
        }
    public:
        void reset();

        // Showmarks
        void setShowMark(pp_int32 c, pp_int32 m, pp_int32 i = 256, pp_int32 p = 128) {
            showMarks[c].pos = m; showMarks[c].intensity = i; showMarks[c].panning = p;
        }
        pp_int32 getShowMarkPos(pp_int32 c) {
            return showMarks[c].pos;
        }

        void clearShowMarks();
        bool hasShowMarks();

        // set scale from 16 to 1024
        void setScale(pp_int32 scale);

        pp_int32 getScale() {
            return xScale;
        }
    private:
        pp_int32 getMaxWidth();

        void adjustScrollbars();

        pp_int32 selectEnvelopePoint(pp_int32 x, pp_int32 y);

        void setEnvelopePoint(pp_int32 index, pp_int32 x, pp_int32 y);

        float calcXScale();
        float calcYScale();

        void updateCurrentPosition(const PPPoint& cp);

        void validate();

        void invokeContextMenu(PPPoint p);

        void executeMenuCommand(pp_int32 commandId);
    public:
        enum EnvelopeToolTypes
        {
            EnvelopeToolTypeNone,
            EnvelopeToolTypeScaleX,
            EnvelopeToolTypeScaleY
        };

        bool invokeToolParameterDialog(EnvelopeToolTypes type);
    private:
        // --- envelope tool responder
        class ToolHandlerResponder : public DialogResponder
        {

            private:
                EnvelopeEditorControl& envelopeEditorControl;
                EnvelopeToolTypes envelopeToolType;
            public:
                ToolHandlerResponder (EnvelopeEditorControl& theEnvelopeEditorControl);

                void setEnvelopeToolType(EnvelopeToolTypes type) {
                    envelopeToolType = type;
                }
                EnvelopeToolTypes getEnvelopeToolType() {
                    return envelopeToolType;
                }

                virtual pp_int32 ActionOkay(PPObject* sender);
                virtual pp_int32 ActionCancel(PPObject* sender);
        };

        friend class ToolHandlerResponder;

        class PPDialogBase * dialog;
        ToolHandlerResponder* toolHandlerResponder;

        bool invokeTool(EnvelopeToolTypes type);

        // Last values
        struct TLastValues
        {
            float scaleEnvelope;
        };

        TLastValues lastValues;

        void resetLastValues()
        {
            lastValues.scaleEnvelope = -1.0f;
        }

        void notifyUpdate()
        {
            PPEvent e(eUpdated);
            eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
        }

        virtual void editorNotification(EditorBase* sender, EditorBase::EditorNotifications notification);
};


#endif